home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / doc / python-apt / html / _static / searchtools.js < prev    next >
Encoding:
Text File  |  2009-03-30  |  12.3 KB  |  468 lines

  1. /**
  2.  * helper function to return a node containing the
  3.  * search summary for a given text. keywords is a list
  4.  * of stemmed words, hlwords is the list of normal, unstemmed
  5.  * words. the first one is used to find the occurance, the
  6.  * latter for highlighting it.
  7.  */
  8.  
  9. jQuery.makeSearchSummary = function(text, keywords, hlwords) {
  10.   var textLower = text.toLowerCase();
  11.   var start = 0;
  12.   $.each(keywords, function() {
  13.     var i = textLower.indexOf(this.toLowerCase());
  14.     if (i > -1)
  15.       start = i;
  16.   });
  17.   start = Math.max(start - 120, 0);
  18.   var excerpt = ((start > 0) ? '...' : '') +
  19.   $.trim(text.substr(start, 240)) +
  20.   ((start + 240 - text.length) ? '...' : '');
  21.   var rv = $('<div class="context"></div>').text(excerpt);
  22.   $.each(hlwords, function() {
  23.     rv = rv.highlightText(this, 'highlight');
  24.   });
  25.   return rv;
  26. }
  27.  
  28. /**
  29.  * Porter Stemmer
  30.  */
  31. var PorterStemmer = function() {
  32.  
  33.   var step2list = {
  34.     ational: 'ate',
  35.     tional: 'tion',
  36.     enci: 'ence',
  37.     anci: 'ance',
  38.     izer: 'ize',
  39.     bli: 'ble',
  40.     alli: 'al',
  41.     entli: 'ent',
  42.     eli: 'e',
  43.     ousli: 'ous',
  44.     ization: 'ize',
  45.     ation: 'ate',
  46.     ator: 'ate',
  47.     alism: 'al',
  48.     iveness: 'ive',
  49.     fulness: 'ful',
  50.     ousness: 'ous',
  51.     aliti: 'al',
  52.     iviti: 'ive',
  53.     biliti: 'ble',
  54.     logi: 'log'
  55.   };
  56.  
  57.   var step3list = {
  58.     icate: 'ic',
  59.     ative: '',
  60.     alize: 'al',
  61.     iciti: 'ic',
  62.     ical: 'ic',
  63.     ful: '',
  64.     ness: ''
  65.   };
  66.  
  67.   var c = "[^aeiou]";          // consonant
  68.   var v = "[aeiouy]";          // vowel
  69.   var C = c + "[^aeiouy]*";    // consonant sequence
  70.   var V = v + "[aeiou]*";      // vowel sequence
  71.  
  72.   var mgr0 = "^(" + C + ")?" + V + C;                      // [C]VC... is m>0
  73.   var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$";    // [C]VC[V] is m=1
  74.   var mgr1 = "^(" + C + ")?" + V + C + V + C;              // [C]VCVC... is m>1
  75.   var s_v   = "^(" + C + ")?" + v;                         // vowel in stem
  76.  
  77.   this.stemWord = function (w) {
  78.     var stem;
  79.     var suffix;
  80.     var firstch;
  81.     var origword = w;
  82.  
  83.     if (w.length < 3)
  84.       return w;
  85.  
  86.     var re;
  87.     var re2;
  88.     var re3;
  89.     var re4;
  90.  
  91.     firstch = w.substr(0,1);
  92.     if (firstch == "y")
  93.       w = firstch.toUpperCase() + w.substr(1);
  94.  
  95.     // Step 1a
  96.     re = /^(.+?)(ss|i)es$/;
  97.     re2 = /^(.+?)([^s])s$/;
  98.  
  99.     if (re.test(w))
  100.       w = w.replace(re,"$1$2");
  101.     else if (re2.test(w))
  102.       w = w.replace(re2,"$1$2");
  103.  
  104.     // Step 1b
  105.     re = /^(.+?)eed$/;
  106.     re2 = /^(.+?)(ed|ing)$/;
  107.     if (re.test(w)) {
  108.       var fp = re.exec(w);
  109.       re = new RegExp(mgr0);
  110.       if (re.test(fp[1])) {
  111.         re = /.$/;
  112.         w = w.replace(re,"");
  113.       }
  114.     }
  115.     else if (re2.test(w)) {
  116.       var fp = re2.exec(w);
  117.       stem = fp[1];
  118.       re2 = new RegExp(s_v);
  119.       if (re2.test(stem)) {
  120.         w = stem;
  121.         re2 = /(at|bl|iz)$/;
  122.         re3 = new RegExp("([^aeiouylsz])\\1$");
  123.         re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
  124.         if (re2.test(w))
  125.           w = w + "e";
  126.         else if (re3.test(w)) {
  127.           re = /.$/;
  128.           w = w.replace(re,"");
  129.         }
  130.         else if (re4.test(w))
  131.           w = w + "e";
  132.       }
  133.     }
  134.  
  135.     // Step 1c
  136.     re = /^(.+?)y$/;
  137.     if (re.test(w)) {
  138.       var fp = re.exec(w);
  139.       stem = fp[1];
  140.       re = new RegExp(s_v);
  141.       if (re.test(stem))
  142.         w = stem + "i";
  143.     }
  144.  
  145.     // Step 2
  146.     re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
  147.     if (re.test(w)) {
  148.       var fp = re.exec(w);
  149.       stem = fp[1];
  150.       suffix = fp[2];
  151.       re = new RegExp(mgr0);
  152.       if (re.test(stem))
  153.         w = stem + step2list[suffix];
  154.     }
  155.  
  156.     // Step 3
  157.     re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
  158.     if (re.test(w)) {
  159.       var fp = re.exec(w);
  160.       stem = fp[1];
  161.       suffix = fp[2];
  162.       re = new RegExp(mgr0);
  163.       if (re.test(stem))
  164.         w = stem + step3list[suffix];
  165.     }
  166.  
  167.     // Step 4
  168.     re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
  169.     re2 = /^(.+?)(s|t)(ion)$/;
  170.     if (re.test(w)) {
  171.       var fp = re.exec(w);
  172.       stem = fp[1];
  173.       re = new RegExp(mgr1);
  174.       if (re.test(stem))
  175.         w = stem;
  176.     }
  177.     else if (re2.test(w)) {
  178.       var fp = re2.exec(w);
  179.       stem = fp[1] + fp[2];
  180.       re2 = new RegExp(mgr1);
  181.       if (re2.test(stem))
  182.         w = stem;
  183.     }
  184.  
  185.     // Step 5
  186.     re = /^(.+?)e$/;
  187.     if (re.test(w)) {
  188.       var fp = re.exec(w);
  189.       stem = fp[1];
  190.       re = new RegExp(mgr1);
  191.       re2 = new RegExp(meq1);
  192.       re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
  193.       if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
  194.         w = stem;
  195.     }
  196.     re = /ll$/;
  197.     re2 = new RegExp(mgr1);
  198.     if (re.test(w) && re2.test(w)) {
  199.       re = /.$/;
  200.       w = w.replace(re,"");
  201.     }
  202.  
  203.     // and turn initial Y back to y
  204.     if (firstch == "y")
  205.       w = firstch.toLowerCase() + w.substr(1);
  206.     return w;
  207.   }
  208. }
  209.  
  210.  
  211. /**
  212.  * Search Module
  213.  */
  214. var Search = {
  215.  
  216.   _index : null,
  217.   _queued_query : null,
  218.   _pulse_status : -1,
  219.  
  220.   init : function() {
  221.       var params = $.getQueryParameters();
  222.       if (params.q) {
  223.           var query = params.q[0];
  224.           $('input[@name="q"]')[0].value = query;
  225.           this.performSearch(query);
  226.       }
  227.   },
  228.  
  229.   /**
  230.    * Sets the index
  231.    */
  232.   setIndex : function(index) {
  233.     var q;
  234.     this._index = index;
  235.     if ((q = this._queued_query) !== null) {
  236.       this._queued_query = null;
  237.       Search.query(q);
  238.     }
  239.   },
  240.  
  241.   hasIndex : function() {
  242.       return this._index !== null;
  243.   },
  244.  
  245.   deferQuery : function(query) {
  246.       this._queued_query = query;
  247.   },
  248.  
  249.   stopPulse : function() {
  250.       this._pulse_status = 0;
  251.   },
  252.  
  253.   startPulse : function() {
  254.     if (this._pulse_status >= 0)
  255.         return;
  256.     function pulse() {
  257.       Search._pulse_status = (Search._pulse_status + 1) % 4;
  258.       var dotString = '';
  259.       for (var i = 0; i < Search._pulse_status; i++)
  260.         dotString += '.';
  261.       Search.dots.text(dotString);
  262.       if (Search._pulse_status > -1)
  263.         window.setTimeout(pulse, 500);
  264.     };
  265.     pulse();
  266.   },
  267.  
  268.   /**
  269.    * perform a search for something
  270.    */
  271.   performSearch : function(query) {
  272.     // create the required interface elements
  273.     this.out = $('#search-results');
  274.     this.title = $('<h2>' + _('Searching') + '</h2>').appendTo(this.out);
  275.     this.dots = $('<span></span>').appendTo(this.title);
  276.     this.status = $('<p style="display: none"></p>').appendTo(this.out);
  277.     this.output = $('<ul class="search"/>').appendTo(this.out);
  278.  
  279.     $('#search-progress').text(_('Preparing search...'));
  280.     this.startPulse();
  281.  
  282.     // index already loaded, the browser was quick!
  283.     if (this.hasIndex())
  284.       this.query(query);
  285.     else
  286.       this.setQuery(query);
  287.   },
  288.  
  289.   query : function(query) {
  290.     // stem the searchterms and add them to the
  291.     // correct list
  292.     var stemmer = new PorterStemmer();
  293.     var searchterms = [];
  294.     var excluded = [];
  295.     var hlterms = [];
  296.     var tmp = query.split(/\s+/);
  297.     var object = (tmp.length == 1) ? tmp[0].toLowerCase() : null;
  298.     for (var i = 0; i < tmp.length; i++) {
  299.       // stem the word
  300.       var word = stemmer.stemWord(tmp[i]).toLowerCase();
  301.       // select the correct list
  302.       if (word[0] == '-') {
  303.         var toAppend = excluded;
  304.         word = word.substr(1);
  305.       }
  306.       else {
  307.         var toAppend = searchterms;
  308.         hlterms.push(tmp[i].toLowerCase());
  309.       }
  310.       // only add if not already in the list
  311.       if (!$.contains(toAppend, word))
  312.         toAppend.push(word);
  313.     };
  314.     var highlightstring = '?highlight=' + $.urlencode(hlterms.join(" "));
  315.  
  316.     console.debug('SEARCH: searching for:');
  317.     console.info('required: ', searchterms);
  318.     console.info('excluded: ', excluded);
  319.  
  320.     // prepare search
  321.     var filenames = this._index.filenames;
  322.     var titles = this._index.titles;
  323.     var terms = this._index.terms;
  324.     var descrefs = this._index.descrefs;
  325.     var modules = this._index.modules;
  326.     var desctypes = this._index.desctypes;
  327.     var fileMap = {};
  328.     var files = null;
  329.     var objectResults = [];
  330.     var regularResults = [];
  331.     $('#search-progress').empty();
  332.  
  333.     // lookup as object
  334.     if (object != null) {
  335.       for (var module in modules) {
  336.         if (module.indexOf(object) > -1) {
  337.           fn = modules[module];
  338.           descr = _('module, in ') + titles[fn];
  339.           objectResults.push([filenames[fn], module, '#module-'+module, descr]);
  340.         }
  341.       }
  342.       for (var prefix in descrefs) {
  343.         for (var name in descrefs[prefix]) {
  344.           if (name.toLowerCase().indexOf(object) > -1) {
  345.             match = descrefs[prefix][name];
  346.             fullname = (prefix ? prefix + '.' : '') + name;
  347.             descr = desctypes[match[1]] + _(', in ') + titles[match[0]];
  348.             objectResults.push([filenames[match[0]], fullname, '#'+fullname, descr]);
  349.           }
  350.         }
  351.       }
  352.     }
  353.  
  354.     // sort results descending
  355.     objectResults.sort(function(a, b) {
  356.       return (a[1] > b[1]) ? -1 : ((a[1] < b[1]) ? 1 : 0);
  357.     });
  358.  
  359.  
  360.     // perform the search on the required terms
  361.     for (var i = 0; i < searchterms.length; i++) {
  362.       var word = searchterms[i];
  363.       // no match but word was a required one
  364.       if ((files = terms[word]) == null)
  365.         break;
  366.       if (files.length == undefined) {
  367.         files = [files];
  368.       }
  369.       // create the mapping
  370.       for (var j = 0; j < files.length; j++) {
  371.         var file = files[j];
  372.         if (file in fileMap)
  373.           fileMap[file].push(word);
  374.         else
  375.           fileMap[file] = [word];
  376.       }
  377.     }
  378.  
  379.     // now check if the files don't contain excluded terms
  380.     for (var file in fileMap) {
  381.       var valid = true;
  382.  
  383.       // check if all requirements are matched
  384.       if (fileMap[file].length != searchterms.length)
  385.         continue;
  386.  
  387.       // ensure that none of the excluded terms is in the
  388.       // search result.
  389.       for (var i = 0; i < excluded.length; i++) {
  390.         if (terms[excluded[i]] == file ||
  391.             $.contains(terms[excluded[i]] || [], file)) {
  392.           valid = false;
  393.           break;
  394.         }
  395.       }
  396.  
  397.       // if we have still a valid result we can add it
  398.       // to the result list
  399.       if (valid)
  400.         regularResults.push([filenames[file], titles[file], '', null]);
  401.     }
  402.  
  403.     // delete unused variables in order to not waste
  404.     // memory until list is retrieved completely
  405.     delete filenames, titles, terms;
  406.  
  407.     // now sort the regular results descending by title
  408.     regularResults.sort(function(a, b) {
  409.       var left = a[1].toLowerCase();
  410.       var right = b[1].toLowerCase();
  411.       return (left > right) ? -1 : ((left < right) ? 1 : 0);
  412.     });
  413.  
  414.     // combine both
  415.     var results = regularResults.concat(objectResults);
  416.  
  417.     // print the results
  418.     var resultCount = results.length;
  419.     function displayNextItem() {
  420.       // results left, load the summary and display it
  421.       if (results.length) {
  422.         var item = results.pop();
  423.         var listItem = $('<li style="display:none"></li>');
  424.         listItem.append($('<a/>').attr(
  425.           'href',
  426.           item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX +
  427.           highlightstring + item[2]).html(item[1]));
  428.         if (item[3]) {
  429.           listItem.append($('<span> (' + item[3] + ')</span>'));
  430.           Search.output.append(listItem);
  431.           listItem.slideDown(5, function() {
  432.             displayNextItem();
  433.           });
  434.         } else if (DOCUMENTATION_OPTIONS.HAS_SOURCE) {
  435.           $.get('_sources/' + item[0] + '.txt', function(data) {
  436.             listItem.append($.makeSearchSummary(data, searchterms, hlterms));
  437.             Search.output.append(listItem);
  438.             listItem.slideDown(5, function() {
  439.               displayNextItem();
  440.             });
  441.           });
  442.         } else {
  443.           // no source available, just display title
  444.           Search.output.append(listItem);
  445.           listItem.slideDown(5, function() {
  446.             displayNextItem();
  447.           });
  448.         }
  449.       }
  450.       // search finished, update title and status message
  451.       else {
  452.         Search.stopPulse();
  453.         Search.title.text(_('Search Results'));
  454.         if (!resultCount)
  455.           Search.status.text(_('Your search did not match any documents. Please make sure that all words are spelled correctly and that you\'ve selected enough categories.'));
  456.         else
  457.             Search.status.text(_('Search finished, found %s page(s) matching the search query.').replace('%s', resultCount));
  458.         Search.status.fadeIn(500);
  459.       }
  460.     }
  461.     displayNextItem();
  462.   }
  463. }
  464.  
  465. $(document).ready(function() {
  466.   Search.init();
  467. });
  468.